package cn.edu.zut.sunday.news.controller;

import cn.edu.zut.sunday.base.controller.GenericController;
import cn.edu.zut.sunday.news.domain.News;
import cn.edu.zut.sunday.news.domain.NewsCategory;
import cn.edu.zut.sunday.news.func.NewsCategoryTransformUtils;
import cn.edu.zut.sunday.news.service.NewsCategoryService;
import cn.edu.zut.sunday.news.service.NewsService;
import cn.edu.zut.sunday.news.vo.NewsQuery;
import com.google.gson.Gson;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Api("mr-新闻接口")
@Slf4j
@RestController
@RequestMapping("news")
@CrossOrigin
public class NewsController extends GenericController<News, Integer, NewsService> {

    private NewsCategoryService newsCategoryServiceImpl;

    @Autowired
    public void setNewsService(NewsService newsService) {
        this.service = newsService;
    }

    @Autowired
    public void setNewsCategoryService(NewsCategoryService newsCategoryServiceImpl) {
        this.newsCategoryServiceImpl = newsCategoryServiceImpl;
    }


    @Override
    protected News addModelCheck(News model,
      HttpServletRequest request) {
        log.info("addModelCheck News: {}", model);
        final boolean success = dealBelongIdInfo(model, request, true);
        if (!success) {return null;}
        return model;
    }

    @Override
    protected News updateModelCheck(Integer id, News model,
      HttpServletRequest request) {
        if (id <= 0) {return null;}
        // 获取样本id信息
        model.setId(id);
        log.info("updateModelCheck News: {}", model);
        final boolean success = dealBelongIdInfo(model, request, false);
        if (!success) {return null;}
        return model;
    }

    @Override
    protected Pageable createPageable(int page, int size) {
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        return PageRequest.of(page, size, sort);
    }

    /**
     * 对分页数据进行转换
     *
     * @param resultPage 从数据库中获取的数据库实体信息
     * @return NewVo
     */
    @Override
    protected Object transform(Page<News> resultPage) {

        return resultPage.map(NewsCategoryTransformUtils.ENTITY_2_VO);
    }

    @Override
    protected Object transform(News entity) {
        return NewsCategoryTransformUtils.ENTITY_2_VO.apply(entity);
    }

    @Override
    protected Specification<News> createSpecification(String requestBodyStr) {
        log.info("requestBodyStr: {}", requestBodyStr);
        final NewsQuery newsQuery = new Gson().fromJson(requestBodyStr, NewsQuery.class);
        log.info("newsQuery: {}", newsQuery);

        if (newsQuery != null) {
            return getNewsSpecification(newsQuery);
        }
        return null;
    }

    /**
     * 分页查询
     */
    private Specification<News> getNewsSpecification(
      NewsQuery newsQuery) {
        String title    = newsQuery.getTitle();
        String category = newsQuery.getCategory();

        return (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();

            if (!StringUtils.isEmpty(title)) {
                Predicate p1 = criteriaBuilder.like(root.get("title"), sqlLike(title));
                list.add(p1);
            }

            if (!StringUtils.isEmpty(category)) {
                // Left join News表和NewsCategory表
                final Join<Object, Object> newCategoryRoot = root.join("newsCategory",
                  JoinType.LEFT);
                // 使用newCategoryRoot进行category条件查询
                Predicate p2 = criteriaBuilder.like(newCategoryRoot.get("category"),
                  sqlLike(category));
                list.add(p2);
            }


            return criteriaBuilder.and(list.toArray(new Predicate[0]));
        };
    }

    /**
     * 从http请求中读取belongId信息，然后根据Id信息查询具体的数据集实体并设置到样本中，这一步是为了建立外键
     *
     * @param model 样本实体信息
     * @param request http请求
     * @return 如果返回false，则表示belongId存在问题，无法执行add/update操作。 如果返回true，则表示设置News#newsCategory属性成功。
     */
    private boolean dealBelongIdInfo(News model, HttpServletRequest request,
      boolean created) {
        // 获取该样本信息所属数据集实体Id
        final String belongIdParam = request.getParameter("belongId");
        if (StringUtils.isEmpty(belongIdParam)) {
            return false;
        }
        final int belongId = Integer.parseInt(belongIdParam);
        log.info("addModelCheck belongId: {}", belongId);
        // 查询该实体信息并且设置到样本的newsCategory属性中
        final NewsCategory newsCategory = this.newsCategoryServiceImpl.findById(belongId);
        if (newsCategory != null) {
            if (!created) {
                // 如果是更新，则获取旧的newsCategory
                final NewsCategory oldNewsCategory = this.service.findById(model.getId())
                  .getNewsCategory();
                // 如果这次修改了belongId，则对旧newsCategory#count - 1
                if (oldNewsCategory.getId() != belongId) {
                    // 旧的数据集 count - 1
                    oldNewsCategory.setCount(oldNewsCategory.getCount() - 1);
                    this.newsCategoryServiceImpl.save(oldNewsCategory);
                }
            }
            // 新的数据集count + 1
            newsCategory.setCount(newsCategory.getCount() + 1);
            this.newsCategoryServiceImpl.save(newsCategory);

            // model设置新的newsCategory
            model.setNewsCategory(newsCategory);
            return true;
        }

        return false;
    }

    /**
     * 这里覆盖父类的delete方法是为了修改NewsCategory#count属性
     *
     * @param id entity id
     */
    @ApiOperation(value = "根据ID信息删除一个数据库中的实体")
    @DeleteMapping(value = "/delete/{id}")
    @Override
    public void delete(@PathVariable Integer id) {
        // 这里删除一个样本信息，所属的NewsCategory#count需要-1
        final News news = this.service.findById(id);
        // 修改count属性
        final NewsCategory newsCategory = news.getNewsCategory();
        newsCategory.setCount(newsCategory.getCount() - 1);
        this.newsCategoryServiceImpl.save(newsCategory);
        // 删除样本
        this.service.delete(id);
    }
}
